package com.oliver.proxy.midjourney.application.service;

import com.oliver.proxy.midjourney.infrastructure.mapper.ProdMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;

@Slf4j
@Service
public class FileProcessingService {
    private final ProdMapper prodMapper;
    private final ExecutorService executor;
    private final AtomicBoolean isProcessing = new AtomicBoolean(false);
    private static final int THREAD_COUNT = 4;
    private static final String PROCESSING_DIR = "/Users/oliver/pic/processing";
    private static final int BATCH_SIZE = 100;

    @Autowired
    public FileProcessingService(ProdMapper prodMapper) {
        this.prodMapper = prodMapper;
        this.executor = Executors.newFixedThreadPool(THREAD_COUNT);
        
        // Ensure processing directory exists
        try {
            Files.createDirectories(Paths.get(PROCESSING_DIR));
        } catch (IOException e) {
            log.error("Failed to create processing directory", e);
        }
    }

    @PostConstruct
    public void init() {
        CompletableFuture.runAsync(this::processFilesInBackground);
    }

    @Scheduled(fixedRate = 6000)
    public void checkAndRestartProcessing() {
        if (!isProcessing.get()) {
            log.info("Restarting file processing...");
            CompletableFuture.runAsync(this::processFilesInBackground);
        }
    }

    private void processFilesInBackground() {
        try {
            if (!isProcessing.compareAndSet(false, true)) {
                return;
            }
            
            log.info("Starting file processing in background thread");
            
            while (true) {
                // Get batch of unprocessed files
                List<Map<String, Object>> prodsToProcess = prodMapper.selectProdsWithAttribute5AndUnmovedFiles();
                
                if (prodsToProcess.isEmpty()) {
                    Thread.sleep(3000);
                    continue;
                }

                // Process files in parallel
                processFilesInParallel(prodsToProcess);
                
                // If we got less than the batch size, wait a bit longer
                if (prodsToProcess.size() < BATCH_SIZE) {
                    Thread.sleep(3000);
                } else {
                    Thread.sleep(1000);
                }
            }
        } catch (Exception e) {
            log.error("Error in file processing", e);
        } finally {
            isProcessing.set(false);
        }
    }

    private void processFilesInParallel(List<Map<String, Object>> prodsToProcess) {
        List<CompletableFuture<Void>> futures = new ArrayList<>();

        for (Map<String, Object> prod : prodsToProcess) {
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> 
                processFile(prod), executor);
            futures.add(future);
        }

        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
    }

    private void processFile(Map<String, Object> prod) {
        String attribute3 = prod.get("attritube_3").toString();
        String attribute5 = prod.get("attritube_5").toString();
        Long dataId = Long.parseLong(prod.get("data_id").toString());

        try {
            Path sourcePath = Paths.get(attribute3);
            if (!Files.exists(sourcePath)) {
//                log.error("Source file not found: {}", attribute3);
//                markAsProcessed(dataId, attribute3); // Mark as processed even if file is missing
                return;
            }

            // Extract original filename (part after last underscore)
            String originalFilename = sourcePath.getFileName().toString();
            int lastUnderscoreIndex = originalFilename.lastIndexOf('-');
            if (lastUnderscoreIndex == -1) {
                log.error("Invalid filename format: {}", originalFilename);
                markAsProcessed(dataId, attribute3); // Mark as processed even if filename is invalid
                return;
            }
            String suffix = originalFilename.substring(lastUnderscoreIndex + 1);

            // Create new filename with attribute5
            String newFilename = attribute5 + "-" + suffix;
            Path targetPath = Paths.get(PROCESSING_DIR, newFilename);

            // Move and rename file
            Files.move(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
            
            // Update the file path and mark as processed
            updateFilePathAndMarkProcessed(dataId, targetPath.toString());
            
            log.info("Successfully moved and renamed file: {} -> {}", sourcePath, targetPath);
        } catch (Exception e) {
            log.error("Error processing file for prod {}: {}", dataId, e.getMessage());
            markAsProcessed(dataId, attribute3); // Mark as processed even if there was an error
        }
    }

    @Transactional
    public void updateFilePathAndMarkProcessed(Long dataId, String newPath) {
        prodMapper.updateAttribute3AndMoveFlag(dataId, newPath);
    }

    @Transactional
    public void markAsProcessed(Long dataId, String currentPath) {
        // Mark as processed without changing the path
        prodMapper.updateAttribute3AndMoveFlag(dataId, currentPath);
    }
} 